﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using System.Xml.Serialization;
using SharePhone.Common;
using SharePhone.Provider.WebServices;
using SharePhone.Search;
using SharePhone.Utility;

namespace SharePhone.Provider
{
    public delegate void SearchMetadataComplete(SearchMetaData metadata);

    public delegate void SearchComplete(SearchResult result);

    public class SearchProvider
    {
        public static void Search(string queryXml, string spUrl, SearchComplete callback, CredentialSettings credentials)
        {
            var client = new SPSearchService(spUrl, credentials);
            client.QueryCompleted +=
                (object sender, RequestCompleteEventArgs e) => { callback(ParseSearchResults(HttpUtility.HtmlDecode(e.Result.Value))); };
            client.QueryAsync(queryXml);
        }

        public static void GetSearchMetaData(string spUrl, SearchMetadataComplete callback,
                                             CredentialSettings credentials)
        {
            var client = new SPSearchService(spUrl, credentials);
            client.GetSearchMetadataCompleted += (object sender, RequestCompleteEventArgs e) =>
                                                     {
                                                         var metadata = new SearchMetaData();

                                                         var serializer = new XmlSerializer(typeof (SearchProperty), "");
                                                         metadata.Scopes = new ObservableCollection<SearchScope>();
                                                         metadata.Properties =
                                                             new ObservableCollection<SearchProperty>();
                                                         foreach (
                                                             XElement element in
                                                                 e.Result.Descendants(XName.Get("Properties", "")))
                                                         {
                                                             var property =
                                                                 (SearchProperty)
                                                                 serializer.Deserialize(element.CreateReader());
                                                             metadata.Properties.Add(property);
                                                         }
                                                         serializer = new XmlSerializer(typeof (SearchScope), "");
                                                         foreach (
                                                             XElement element in
                                                                 e.Result.Descendants(XName.Get("Scopes", "")))
                                                         {
                                                             var scope =
                                                                 (SearchScope)
                                                                 serializer.Deserialize(element.CreateReader());
                                                             metadata.Scopes.Add(scope);
                                                         }
                                                         callback(metadata);
                                                     };
            client.GetSearchMetadataAsync();
        }

        #region Internal parsers

        private static SearchResult ParseSearchResults(string xml)
        {
            var res = new SearchResult();
            const string docNS = "urn:Microsoft.Search.Response.Document";
            XName totalAvailableName = XName.Get("TotalAvailable", "urn:Microsoft.Search.Response");
            XName resultsName = XName.Get("Results", "urn:Microsoft.Search.Response");
            XName documentName = XName.Get("Document", docNS);
            XName propertiesName = XName.Get("Properties", "urn:Microsoft.Search.Response.Document.Document");
            XDocument doc = XDocument.Parse(xml);
            XElement status = doc.Descendants(XName.Get("Status", "urn:Microsoft.Search.Response")).First();
            res.Status = status.Value;
            XElement debugMsg =
                doc.Descendants(XName.Get("DebugErrorMessage", "urn:Microsoft.Search.Response")).FirstOrDefault();
            res.DebugErrorMessage = (debugMsg == null) ? string.Empty : debugMsg.Value;
            if (res.Status != "SUCCESS")
            {
                return res;
            }
            res.TotalAvailable = Int32.Parse(doc.Descendants(totalAvailableName).First().Value);
            XElement results = doc.Descendants(resultsName).First();
            var docs = new List<SearchDocument>();
            foreach (XElement document in results.Elements(documentName))
            {
                var sDoc = new SearchDocument();
                sDoc.Properties = new List<SearchDocumentProperty>();
                XElement properties = document.Element(propertiesName);
                if (properties != null)
                {
                    foreach (XNode property in properties.Nodes())
                    {
                        string name =
                            ((XElement) property).Element(XName.Get("Name",
                                                                    "urn:Microsoft.Search.Response.Document.Document")).
                                Value;
                        string value =
                            ((XElement) property).Element(XName.Get("Value",
                                                                    "urn:Microsoft.Search.Response.Document.Document")).
                                Value;
                        string type =
                            ((XElement) property).Element(XName.Get("Type",
                                                                    "urn:Microsoft.Search.Response.Document.Document")).
                                Value;
                        var prop = new SearchDocumentProperty {Name = name, Type = type, Value = value};
                        sDoc.Properties.Add(prop);
                    }
                    sDoc.Title = sDoc.Properties.Where(p => p.Name == "TITLE").First().Value;
                    sDoc.Relevance = Int32.Parse(sDoc.Properties.Where(p => p.Name == "RANK").First().Value);
                }
                else
                {
                    int relevance = Int32.Parse(XmlUtility.GetAttributeValue(document, "relevance"));
                    sDoc.Title = document.Element(XName.Get("Title", docNS)).Value;
                    sDoc.Description = document.Element(XName.Get("Description", docNS)).Value;
                    sDoc.Date = DateTime.Parse(document.Element(XName.Get("Date", docNS)).Value);
                }
                XElement action = document.Element(XName.Get("Action", docNS));
                sDoc.LinkUrl = action.Element(XName.Get("LinkUrl", docNS)).Value;
                docs.Add(sDoc);
            }
            res.Results = docs;
            return res;
        }

        //private static SearchMetaData ParseMetadataResult(ArrayOfXElement result) {
        //    //TODO: implement
        //    return null;
        //}
        //private List<SearchProperty> GetProperties(DataTable propertiesTable) {
        //    List<SearchProperty> props = new List<SearchProperty>();
        //    foreach (DataRow row in propertiesTable.Rows) {
        //        SearchProperty property = new SearchProperty();
        //        property.Name = row["Name"].ToString();
        //        property.Description = row["Description"].ToString();
        //        property.Type = row["Type"].ToString();
        //        property.Retrievable = Boolean.Parse(row["Retrievable"].ToString());
        //        property.FullTextQueriable = Boolean.Parse(row["FullTextQueriable"].ToString());
        //        props.Add(property);
        //    }
        //    return props;
        //}
        //private List<SearchScope> GetScopes(DataTable scopeTable) {
        //    List<SearchScope> scopes = new List<SearchScope>();
        //    foreach (DataRow row in scopeTable.Rows) {
        //        SearchScope scope = new SearchScope();
        //        scope.Name = row["Name"].ToString();
        //        scope.Description = row["Description"].ToString();
        //        scopes.Add(scope);
        //    }
        //    return scopes;
        //}

        #endregion
    }
}